package org.sunspotworld.demo;

import com.sun.spot.sensorboard.EDemoBoard;
import com.sun.spot.sensorboard.peripheral.ILightSensor;
import com.sun.spot.sensorboard.peripheral.ILightSensorThresholdListener;
import com.sun.spot.sensorboard.peripheral.IAccelerometer3D;
import com.sun.spot.sensorboard.peripheral.IAccelerometer3DThresholdListener;
import com.sun.spot.sensorboard.peripheral.ISwitch;
import com.sun.spot.sensorboard.peripheral.ISwitchListener; 
import com.sun.spot.util.Utils;
import java.io.IOException;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class SunSpotAudio extends MIDlet implements ILightSensorThresholdListener, 
                                                    ISwitchListener, 
                                                    IAccelerometer3DThresholdListener 
{

    private final double ACCEL_INCREMENT_THRESHOLD = 1.1;
    private final double ACCEL_DECREMENT_THRESHOLD = 0.5;
    private final double ACCEL_MINVAL_THRESHOLD = 0.3;
    private final int TEMPO_INCREMENT_DECREMENT = 55;
    private final int REST_INCREMENT_DECREMENT = 10;
    private final int MIN_TEMPO = 230;
    private final int MAX_TEMPO = 3000;
    private AudioThread m_audioThread;
    private ISwitch [] m_switches = EDemoBoard.getInstance().getSwitches();
    private ILightSensor m_lightSensor = EDemoBoard.getInstance().getLightSensor();  
    private IAccelerometer3D m_accel = EDemoBoard.getInstance().getAccelerometer();
    private int m_sampleDelay = 750;
    
    // m_tempoIncrementDecrement is the value by which the tempo is increased
    //   or decreased depending on the acceleration input
    private int m_tempo = 1000;
    
    private int m_rest = 100;
    
    private boolean m_useLog = true;
    private boolean m_useLightShow = true;
    
    private void initAndRun() throws IOException {
        
        // initialize light sensor callbacks and thresholds
        m_lightSensor.addILightSensorThresholdListener(this); 
        m_lightSensor.setThresholds(0, 740);                  
        m_lightSensor.enableThresholdEvents(true);   
        
        // initialize acceleromter 
        m_accel.setRestOffsets();
        m_accel.addIAccelerometer3DThresholdListener(this);
        double low = m_accel.getLowThreshold(m_accel.X_AXIS, true);
        double high = m_accel.getHighThreshold(m_accel.X_AXIS, true);
        m_accel.setThresholds(m_accel.X_AXIS, low, high, true);
        
        // initialize switch released callback
        m_switches[0].addISwitchListener(this);
        //m_switches[1].addISwitchListener(this);
                
        // spawn a new audio thread w/ default pitch, tempo, and rest values
        m_audioThread = new AudioThread(getStartPitch(), getStartTempo(), getStartRest());
        m_audioThread.IsLogEnabled(m_useLog);
        m_audioThread.IsLightShowEnabled(m_useLightShow);
        Thread t = new Thread(m_audioThread);
        t.start();
        
        // main thread loop
        while(true)
        {
            // wait a while and log if necessary
            Utils.sleep(m_sampleDelay);
            if(m_useLog)
            {
                System.out.println("[reading enviro every " + Integer.toString(m_sampleDelay) + "ms...]");
            }
            
            // set the audio thread's values
            update(m_audioThread);
        }
    }
    
    public int getStartPitch() throws IOException
    {
        // bring light sensor way down (from [0 - 740] to approximate 
        // output range [0 - 100?]
        return m_lightSensor.getValue() / 6;
    }
    
    public int getStartTempo() throws IOException
    {
        // sample the accelerometer's x-axis
        double x = Math.abs(m_accel.getRelativeAccelX());
        
        if(x >= ACCEL_INCREMENT_THRESHOLD)
        {   
            // must "speed up" so we decrease the time between period (this is confusing)
            // m_tempo should be renamed since tempo is normally given in BPM.
            m_tempo -= TEMPO_INCREMENT_DECREMENT;
            
            if(m_tempo < MIN_TEMPO)
                m_tempo = MIN_TEMPO;
        }
        else if(x > ACCEL_MINVAL_THRESHOLD && x <= ACCEL_DECREMENT_THRESHOLD)
        {
            // we are "rocking the baby" so we should slow down
            // (credit to David Koelle, author of JFugue, for this idea)
            m_tempo += TEMPO_INCREMENT_DECREMENT;
            
            if(m_tempo > MAX_TEMPO)
                m_tempo = MAX_TEMPO;
        }
        else {}
        
        return m_tempo;
    }
    
    public int getStartRest()
    {
        // TODO: Add Temperature sensor --> higher values give closer to 100%
        //       while lower values give closer to "min % playing value" of ~20%
        
        return m_rest;
    }
    
    private void update(AudioThread t) throws IOException
    {
        t.setStartPitch(getStartPitch());
        t.setStartTempo(getStartTempo());
        t.setStartRest(getStartRest());
        
        // Toggle the scale ONLY on update and either switch pressed
        if(m_switches[1].isClosed())
        {
            m_rest += REST_INCREMENT_DECREMENT;
            
            if(m_rest > 100)
                m_rest = 10;
        }
    }
    
    // MIDlet
    protected void startApp() throws MIDletStateChangeException {
        try 
        {
            initAndRun();
        } 
        catch (IOException ex) 
        { 
            ex.printStackTrace();
        }
    }
    protected void pauseApp() { }
    protected void destroyApp(boolean unconditional) throws MIDletStateChangeException { }

    // ILightSensorThresholdListener
    public void thresholdExceeded(ILightSensor arg0, int arg1) 
    {
    
    }
    public void thresholdChanged(ILightSensor arg0, int arg1, int arg2) 
    { 
    
    }

    // ISwitchListener
    public void switchPressed(ISwitch arg0) 
    {
    
    }

    public void switchReleased(ISwitch arg0) {
        m_audioThread.toggleScaleType();
    }
    
    // IAccelerometer3DThresholdListener
    public void thresholdExceeded(IAccelerometer3D arg0, int arg1, double arg2, boolean arg3) 
    {
        
    }

    public void thresholdChanged(IAccelerometer3D arg0, int arg1, double arg2, double arg3, boolean arg4)
    {
        
    }
}
